#include "httprequest.h"

#include <algorithm>

// 预定义的路径
const std::unordered_set<std::string> HttpRequest::defaultHtml_
{
    "/index", "/register", "/login",
    "/welcome", "/video", "/picture",
};

// 预定义的 HTML 文件路径及其标签值
const std::unordered_map<std::string, int> HttpRequest::defaultHtmlTag_
{
    {"/register.html", 0}, // 注册
    {"/login.html", 1},    // 登录
};

HttpRequest::HttpRequest()
{
    init();
}

// 初始化
void HttpRequest::init()
{
    method_ = path_ = version_ = body_ = "";
    parseState_ = PARSE_STATE::REQUEST_LINE;
    header_.clear();
    post_.clear();
}

// 是否保持长连接
bool HttpRequest::isKeepAlive() const
{
    if(header_.count("Connection") == 1) {
        return header_.find("Connection")->second == "keep-alive" && version_ == "1.1";
    }
    return false;
}

// 进行报文解析
bool HttpRequest::parse(Buffer& buff)
{
    const char CRLF[] = "\r\n";
    if(buff.readableBytes() <= 0) 
        return false;

    // 进行报文的解析
    while(buff.readableBytes() && parseState_ != PARSE_STATE::FINISH)
    {
        // 从缓冲区中获取请求行/请求头/请求头
        const char* lineEnd = std::search(buff.peek(), buff.beginWriteConst(), CRLF, CRLF + 2);
        std::string line(buff.peek(), lineEnd);
        // 根据解析的状态不同进行不同部分的解析
        switch (parseState_)
        {
        case PARSE_STATE::REQUEST_LINE:
            if(!parseRequestLine(line)) 
                return false;
            parseRequestPath();
            break;
        case PARSE_STATE::HEADERS:
            parseRequestHeader(line);
            if(buff.readableBytes() <= 2)
                parseState_ = PARSE_STATE::FINISH;
            break;
        case PARSE_STATE::BODY:
            parseRequestBody(line);
            break;
        default:
            break;
        }

        // 解析完成
        if(lineEnd == buff.beginWrite()) 
            break;
        buff.retrieveUntil(lineEnd + 2);
    }

    LOG_DEBUG("[%s], [%s], [%s]", method_.c_str(), path_.c_str(), version_.c_str());
    return true;
}

//解析请求路径
void HttpRequest::parseRequestPath() 
{
    // 根路径设置为默认首页
    if(path_ == "/") {
        path_ = "/index.html";
    } else {
        for(auto &item : defaultHtml_) {
            if(item == path_) {
                path_ += ".html";
                break;
            }
        }
    }
}

// 解析请求行
bool HttpRequest::parseRequestLine(const std::string& line)
{
    // 匹配三组非空字符组 例如 GET /index.html HTTP/1.1
    std::regex patten("^([^ ]*) ([^ ]*) HTTP/([^ ]*)$");
    std::smatch subMatch;
    // 检查字符串是否与正则表达式匹配，并将匹配结果存储到 subMatch 中
    if(std::regex_match(line, subMatch, patten)) {
        method_ = subMatch[1];
        path_   = subMatch[2];
        version_= subMatch[3];
        parseState_ = PARSE_STATE::HEADERS;
        LOG_DEBUG("parse request line!");
        return true;
    }
    LOG_ERROR("RequestLine Error!");
    return false;
}

// 解析请求头
void HttpRequest::parseRequestHeader(const std::string& line)
{
    // 匹配键值对 (.*)任意字符出现零次或多次 ?表示尽可能少的匹配
    std::regex patten("^([^:]*): ?(.*)$");
    std::smatch subMatch;
    if(std::regex_match(line, subMatch, patten)) {
        header_[subMatch[1]] = subMatch[2];
    } else {
        parseState_ = PARSE_STATE::BODY;
    }
    LOG_DEBUG("parse request header!");
}

// 解析请求体
void HttpRequest::parseRequestBody(const std::string& line)
{
    body_ = line;
    parseRequestPost();
    parseState_ = PARSE_STATE::FINISH;
    LOG_DEBUG("parse request body, body : %s, len : %d", line.c_str(), line.size());
}

// 将一个十六进制字符转换为对应的整数值
int HttpRequest::converHex(char ch) 
{
    if(ch >= 'A' && ch <= 'F')  return ch - 'A' + 10;
    if(ch >= 'a' && ch <= 'f')  return ch - 'a' + 10;
    return ch;
}

// 解析post请求
void HttpRequest::parseRequestPost()
{
    // 判断请求方法是否为POST，并且判断表单编码方式
    // 要求键和值之间用等号 = 连接，不同的键值对之间用 & 连接
    // 例如 username=john&password=123456
    if(method_ == "POST" && header_["Content-Type"]
        == "application/x-www-form-urlencoded") 
    {
        // 对请求体中的表单数据进行解析
        parseFromUrlencoded();

        //  检查路径是否存在于映射表中
        if(defaultHtmlTag_.count(path_)) {
            // 获取对应的标签
            int tag = defaultHtmlTag_.find(path_)->second;
            LOG_DEBUG("Parse request post, Tag : %d", tag);
            // 判断是否需要进行用户验证
            if(tag == 0 || tag == 1) {
                bool isLogin = (tag == 1);
                // 用户验证
                if(userVerify(post_["username"], post_["password"], isLogin)) {
                    path_ = "/welcome.html";
                } else {
                    path_ = "/error.html";
                }
            }
        }
    }
}

// 解析POST 请求中以 application/x-www-form-urlencoded 格式编码的请求体数据
void HttpRequest::parseFromUrlencoded()
{
    if(body_.size() == 0) 
        return;
    
    std::string key, value;
    // 存储 URL 编码转换后的十进制数值
    int num = 0;
    int n = body_.size();
    int i = 0, j = 0;

    for(; i < n; ++i) {
        char ch = body_[i];
        switch (ch)
        {
        // =代表遍历完了一个键
        case '=':
            key = body_.substr(j, i - j);
            j = i + 1;
            break;
        // +在这个编码中通常表示空格
        case '+':
            body_[i] = ' ';
            break;
        // %后面跟着两个十六进制字符代表特殊字符
        case '%':
            num = converHex(body_[i + 1]) * 16 + converHex(body_[i + 2]);
            body_[i + 2] = num % 10 + '0';
            body_[i + 1] = num / 10 + '0';
            i += 2;
            break;
        // &表示遍历完了一个值
        case '&':
            value = body_.substr(j, i - j);
            j = i + 1;
            post_[key] = value;
            LOG_DEBUG("parse Post k-v : %s = %s", key.c_str(), value.c_str());
            break;
        default:
            break;
        }
    }

    assert(j <= i);
    // 存储最后一个键值对
    if(post_.count(key) == 0 && j < i) {
        value = body_.substr(j, i - j);
        post_[key] = value;
        LOG_DEBUG("parse Post k-v : %s = %s", key.c_str(), value.c_str());
    }
}

// 进行用户的验证
bool HttpRequest::userVerify(const std::string& name, 
                            const std::string& pwd, bool isLogin)   
{
    if(name == "" || pwd == "") return false;
    LOG_INFO("verify name : %s, pwd : %s", name.c_str(), pwd.c_str());

    // 获取一个数据库的连接
    MYSQL* sql;
    SqlConnRAII(&sql, SqlConnPool::instance());
    assert(sql);

    bool flag = false;      // 标记验证信息
    // unsigned int j = 0;     // 存储查询结果的字段数量
    char order[256] = {0};  // 存储 SQL 查询语句

    // MYSQL_FIELD *fields = nullptr;  // 存储查询结果的信息
    MYSQL_RES *res = nullptr;       // 存储查询结果集

    if(!isLogin) 
        flag = true;
    // 从 user 表中查询指定用户名
    snprintf(order, 256, "SELECT username, password FROM user WHERE username='%s' LIMIT 1", name.c_str());
    LOG_DEBUG("%s", order);

    // 执行 SQL 查询语句
    if(mysql_query(sql, order)) {
        mysql_free_result(res);
        return false;
    }
    // 存储查询结果
    res = mysql_store_result(sql);
    // j = mysql_num_fields(res);
    // 获取查询结果的字段信息
    // fields = mysql_fetch_fields(res);

    // 逐行遍历查询结果集
    while(MYSQL_ROW row = mysql_fetch_row(res)) {
        LOG_DEBUG("MYSQL ROW : %s %s", row[0], row[1]);
        std::string password(row[1]);

        // 用户登录
        if(isLogin) {
            if(pwd == password) {
                flag = true;
            } else {
                flag = false;
                LOG_DEBUG("pwd error!");
            }
        } else {
            // 用户注册并且该用户名已经被注册过
            flag = false;
            LOG_DEBUG("user used!");
        }
    }

    mysql_free_result(res);
    // 用户注册并且用户名没有被使用
    if(!isLogin && flag == true) {
        LOG_DEBUG("regirster!");
        memset(order, 0, sizeof(order));
        // 构建插入新用户记录的 SQL 语句
        snprintf(order, 256,"INSERT INTO user(username, password) VALUES('%s','%s')", name.c_str(), pwd.c_str());
        LOG_DEBUG( "%s", order);
        // 执行sql语句
        if(mysql_query(sql, order)) { 
            LOG_DEBUG( "Insert error!");
            flag = false; 
        }
        flag = true;
    }

    // 释放连接
    SqlConnPool::instance()->freeConn(sql);
    LOG_DEBUG( "UserVerify success!!");
    return flag;
}

std::string HttpRequest::path() const
{
    return path_;
}

std::string& HttpRequest::path()
{
    return path_;
}
        
std::string HttpRequest::method() const 
{
    return method_;
}

std::string HttpRequest::version() const
{
    return version_;
}

std::string HttpRequest::getPost(const char* key) const
{
    assert(key != nullptr);
    if(post_.count(key) == 1) {
        return post_.find(key)->second;
    }
    return "";
}
    
std::string HttpRequest::getPost(const std::string& key) const
{
    assert(key != "");
    if(post_.count(key) == 1) {
        return post_.find(key)->second;
    }
    return "";
}
